/**
 * @file dogzwindow.cpp
 *
 * Klasa głównego okna programu - implementacja.
 *
 * @author Zbigniew Siciarz
 * @date 2009
 * @version 2.0.0
 * @since 2.0.0
 */

#include "DogzWindow.h"

#include "Dogz08ImportDialog.h"
#include "wizards/intro/IntroWizard.h"
#include "MessageDialog.h"
#include "PreferencesDialog.h"
#include <QtGui>
#include <QtNetwork>

/**
 * Konstruktor głównego okna.
 *
 * Inicjalizuje interfejs użytkownika, wczytuje ustawienia aplikacji.
 * Ponadto włącza podpowiadanie nazw gwiazd oraz prosty czytnik RSS.
 *
 * @param parent obiekt rodzica
 */
DogzWindow::DogzWindow(QWidget *parent)
    : QMainWindow(parent), networkManager(0), starManager(0),
    visualObsDialog(0), starsDialog(0), observationsListDialog(0), rss(0)
{
	ui.setupUi(this);
	setCentralWidget(ui.stackMainPanels);
	ui.stackMainPanels->setCurrentWidget(ui.pageHome);
    setWindowTitle(qApp->applicationName() + " " + qApp->applicationVersion());
    MessageDialog::setParentForAllMessages(this);

	readSettings();
    createActions();
    createMenus();
    createTools();

    // przyciski na głównym ekranie
    connect(ui.btnShowAddObservations, SIGNAL(clicked()),
            this, SLOT(showAddObservations()));
    connect(ui.btnShowObservationsList, SIGNAL(clicked()),
            this, SLOT(showObservationsList()));

    QStringList starNames = Dogz::StarManager::getNames();
    if (!starNames.isEmpty())
    {
        QCompleter* completer = new QCompleter(starNames, this);
        completer->setCaseSensitivity(Qt::CaseInsensitive);
        ui.editQuickStarSearch->setCompleter(completer);
    }

    initNetwork();

    statusBar()->showMessage(tr("Gotowy"), 3000);
}

/**
 * Zdarzenie zamykania okna.
 *
 * Przesłania metodę zdefiniowaną w klasie QWidget. Przed zamknięciem okna
 * (co w przypadku okna głównego jest równoznaczne z zamknięciem programu)
 * zapisuje ustawienia aplikacji.
 *
 * @param event obiekt informacji o zdarzeniu zamykania okna
 */
void DogzWindow::closeEvent(QCloseEvent* event)
{
    writeSettings();
    event->accept();
}

/**
 * Tworzy akcje aplikacji.
 *
 * Uzupełnia kod automatycznie wygenerowany przez Qt Designera o wskazówki
 * wyświetlane na pasku stanu i skróty klawiszowe oraz łączy sygnały
 * wyzwolenia akcji z odpowiednimi slotami klasy okna.
 */
void DogzWindow::createActions()
{
    ui.actionTest->setStatusTip(tr("Testowanie aplikacji"));
    connect(ui.actionTest, SIGNAL(triggered()), this, SLOT(test()));

    ui.actionAddObservations->setStatusTip(tr("Dodaj nowe obserwacje"));
    connect(ui.actionAddObservations, SIGNAL(triggered()),
            this, SLOT(showAddObservations()));

    ui.actionBrowseObservations->setStatusTip(
            tr("Przeglądaj bazę danych swoich obserwacji"));
    connect(ui.actionBrowseObservations, SIGNAL(triggered()),
            this, SLOT(showObservationsList()));

    ui.actionStarsWindow->setStatusTip(tr("Pokaż lokalną bazę gwiazd"));
    connect(ui.actionStarsWindow, SIGNAL(triggered()),
            this, SLOT(showStarsWindow()));

    ui.actionImportOldDogz->setStatusTip(tr("Importuj obserwacje"));
    connect(ui.actionImportOldDogz, SIGNAL(triggered()),
            this, SLOT(importFromOldDogz()));
    ui.actionPreferences->setStatusTip(tr("Okno ustawień programu"));
    connect(ui.actionPreferences, SIGNAL(triggered()),
            this, SLOT(showPreferences()));

    ui.actionAbout->setStatusTip(tr("Informacje o programie"));
    connect(ui.actionAbout, SIGNAL(triggered()), this, SLOT(about()));
    ui.actionAboutQt->setStatusTip(tr("Informacje o bibliotece Qt"));
    connect(ui.actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
}

/**
 * Tworzy te części menu, których nie da się wyklikać w Qt Designerze.
 */
void DogzWindow::createMenus()
{
}

/**
 * Inicjalizuje kontrolki bocznego panelu narzędzi.
 */
void DogzWindow::createTools()
{
    QDateTime currentDate = QDateTime::currentDateTime();
    ui.editDateFrom->setDateTime(currentDate);

    ui.editJdFrom->setValidator(new QDoubleValidator(this));
    ui.editJdFrom->setText(JdTools::dateToJd(currentDate, JdTools::AsString));
    ui.progressQuickStar->hide();

    QStringList recentObsHeaders;
    recentObsHeaders << tr("JD") << tr("Data (UT)") << tr("Jasność") << tr("Filtr");
    ui.tblRecentAavsoObs->setHorizontalHeaderLabels(recentObsHeaders);
    ui.tblRecentAavsoObs->resizeColumnsToContents();
    ui.tblRecentAavsoObs->horizontalHeader()->setStretchLastSection(true);
}

/**
 * Inicjalizuje obsługę sieci i RSS.
 */
void DogzWindow::initNetwork()
{
    networkManager = new QNetworkAccessManager(this);
    starManager = new Dogz::StarManager(this);
    starManager->setNetworkManager(networkManager);
    connect(starManager, SIGNAL(starFound(Dogz::Star)),
            this, SLOT(gcvsLookupFinished(Dogz::Star)), Qt::QueuedConnection);
    connect(starManager, SIGNAL(gcvsError(QString)), this, SLOT(gcvsError(QString)));
    connect(starManager, SIGNAL(aavsoLookupFinished(QList<Dogz::Observation*>)),
            this, SLOT(aavsoLookupFinished(QList<Dogz::Observation*>)));
    connect(starManager, SIGNAL(aavsoError(QString)), this, SLOT(aavsoError(QString)));
    QSettings settings;
    if (settings.value("network/enableAavsoRss", true).toBool() == false)
    {
        ui.txtRssAavso->setHtml(tr("<center>Pobieranie aktualności z AAVSO "
            "zostało wyłączone. Możesz to zmienić w ustawieniach programu "
            "w sekcji \"Opcje internetowe\" (wymaga ponownego uruchomienia "
            "programu).</center>"));
        return;
    }
    rss = new RssReader(this);
    rss->setNetworkManager(networkManager);
    rss->setTemplate("<a href=\"%link%\"><b>%title%</b></a><br>"
            "<span style=\"color: #666; font-size: small;\">%date%</span>"
            "%description%<br><br>");
    connect(rss, SIGNAL(parsingFinished()), this, SLOT(rssReadFinished()));
    rss->downloadFeed(settings.value("network/aavsoFeedUrl").toString());
}

/**
 * Odczytuje ustawienia aplikacji.
 *
 * Metodę tę wywołuje się w konstruktorze głównego okna po utworzeniu wszystkiego
 * co związane z GUI. Jej zadaniem jest przywrócenie wymiarów i położenia okna,
 *  stanu pasków narzędzi i doków itp...
 */
void DogzWindow::readSettings()
{
    QSettings settings;
    restoreGeometry(settings.value("geometry").toByteArray());
    restoreState(settings.value("state").toByteArray());
}

/**
 * Zapisuje ustawienia programu.
 *
 * Zapisuje informacje o położeniu i wymiarach okna, stanie pasków narzędzi,
 * doków itp...
 */
void DogzWindow::writeSettings()
{
    QSettings settings;
    settings.setValue("geometry", saveGeometry());
    settings.setValue("state", saveState());
}

/**
 * Wypisuje dany tekst na "konsoli" programu.
 *
 * Póki nie ma konsoli, wyświetla okienko z informacją.
 *
 * @param msg wiadomość
 */
void DogzWindow::logMessage(const QString& msg)
{
    MessageDialog::showInformation(tr("Informacja"), msg);
}

/**
 * Pokazuje okienko ustawień.
 */
void DogzWindow::showPreferences()
{
    PreferencesDialog dlg(this);
    if (dlg.exec())
        dlg.saveSettings();
}

/**
 * Slot przycisku "O programie."
 */
void DogzWindow::about()
{
    MessageDialog::showInformation(tr("DOGZ - o programie"), tr(
            "<center>DOGZ - <b>D</b>ziennik <b>O</b>bserwatora <b>G</b>wiazd "
            "<b>Z</b>miennych</center>"
            "<p>DOGZ jest programem wspierającym opracowywanie obserwacji "
            "gwiazd zmiennych. Ułatwia współpracę z internetowymi bazami "
            "danych takimi jak AAVSO, czy PTOGZ.</p>"
            "<p>Wersja: %1</p>"
            "<br><center>(c) Zbigniew Siciarz 2009</center>").
                arg((qApp->applicationVersion())));
}

/**
 * Slot przycisku testowego.
 */
void DogzWindow::test()
{
    IntroWizard* wizard = new IntroWizard(this);
    wizard->show();
}



/**
 * Reaguje na zakonczenie odczytu nowości z RSS-a.
 */
void DogzWindow::rssReadFinished()
{
    ui.txtRssAavso->setText(rss->getParsedFeed());
}


/**
 * Pokazuje okno dodawania nowych obserwacji.
 */
void DogzWindow::showAddObservations()
{
    if (!visualObsDialog)
        visualObsDialog = new VisualObservationDialog(networkManager, this);

    visualObsDialog->show();
    visualObsDialog->raise();
    visualObsDialog->activateWindow();
}

/**
 * Pokazuje okienko podglądu tabeli gwiazd.
 */
void DogzWindow::showStarsWindow()
{
    if (!starsDialog)
        starsDialog = new StarsDialog(this);

    starsDialog->show();
    starsDialog->raise();
    starsDialog->activateWindow();
}

/**
 * Pokazuje okno przeglądania obserwacji.
 */
void DogzWindow::showObservationsList()
{
    if (!observationsListDialog)
        observationsListDialog = new ObservationsListDialog(starManager, this);

    observationsListDialog->show();
    observationsListDialog->raise();
    observationsListDialog->activateWindow();
}

/**
 * Pokazuje okienko importu ze starego DOGZ-a.
 */
void DogzWindow::importFromOldDogz()
{
    Dogz08ImportDialog dlg;
    dlg.exec();
}

/**
 * Przelicza datę i czas na JD w momencie zmiany wartości pola edycyjnego.
 *
 * @param date wybrana data
 */
void DogzWindow::on_editDateFrom_dateTimeChanged(const QDateTime& date)
{
    ui.editJdTo->setText(JdTools::dateToJd(date, JdTools::AsString));
}

/**
 * Ustawia bieżącą datę w polu konwersji data->JD.
 */
void DogzWindow::on_btnDateFromNow_clicked()
{
    ui.editDateFrom->setDateTime(QDateTime::currentDateTime());
}

/**
 * Przelicza JD na datę.
 *
 * Zapisuje datę w takim samym formacie jak ta w polu powyżej.
 *
 * @param text wartość JD odczytana z pola edycyjnego, jako string
 */
void DogzWindow::on_editJdFrom_textChanged(const QString& text)
{
    ui.editDateTo->setText(JdTools::jdToDate(text).toString(
            ui.editDateFrom->displayFormat()));
}

/**
 * Ustawia bieżące JD w polu konwersji JD->data.
 */
void DogzWindow::on_btnJdFromNow_clicked()
{
    ui.editJdFrom->setText(JdTools::dateToJd(QDateTime::currentDateTime(),
            JdTools::AsString));
}

/**
 * Szuka informacji o gwieździe w GCVS i AAVSO.
 *
 * Jeśli pole jest puste, natychmiast zwraca nie robiąc nic. W przeciwnym
 * wypadku normalizuje nazwę gwiazdy, zgłasza żądania wyszukiwania w GCVS
 * oraz w najnowszych obserwacjach AAVSO.
 *
 * Wypisuje informację o wyszukiwaniu w okienku podglądu i pokazuje pasek
 * postępu.
 */
void DogzWindow::on_btnQuickStarSearch_clicked()
{
    if (ui.editQuickStarSearch->text().isEmpty())
    {
        MessageDialog::showInformation(tr("Wyszukiwanie gwiazdy"),
                tr("Podaj nazwę gwiazdy!"));
        return;
    }

    ui.editQuickStarSearch->setText(Dogz::Star::normalizeStarName(
            ui.editQuickStarSearch->text()));

    // wyszukiwanie w GCVS
    starManager->findStarByName(ui.editQuickStarSearch->text());
    ui.tabQuickStarSearch->setCurrentIndex(0);
    ui.txtQuickStarResults->setHtml(tr("<h2>Wyszukiwanie: %1</h2>"
            "<p>Proszę czekać...</p>").arg(ui.editQuickStarSearch->text()));
    ui.progressQuickStar->show();

    // najnowsze obserwacje z AAVSO
    starManager->getAavsoObservations(ui.editQuickStarSearch->text());
    ui.tabQuickStarSearch->setTabText(1, tr("AAVSO"));
    ui.tblRecentAavsoObs->clearContents();
    ui.tblRecentAavsoObs->setRowCount(0);
    ui.lblRecentAavsoObs->setText(tr("Wyszukiwanie, proszę czekać..."));
}

/**
 * Reaguje na przyjście danych z katalogu GCVS online.
 *
 * Ukrywa pasek postępu i prezentuje wyniki lub komunikat o błędzie, ponadto
 * powiadamia użytkownika krótkim dźwiękiem.
 *
 * @param star odnaleziona gwiazda (może być invalid)
 */
void DogzWindow::gcvsLookupFinished(Dogz::Star star)
{
    ui.progressQuickStar->hide();

    //  dodaj wyniki do podglądu
    if (star.isValid())
    {
        QString info = tr("<h2>%1</h2>"
                "<h3>Typ zmienności: %2</h3>"
                "<h3>Zakres: %3 - %4 mag.").
                arg(star.getName()).
                arg(star.getType()).
                arg(star.getMaxMagnitude(), 0, 'f', 2).
                arg(star.getMinMagnitude(), 0, 'f', 2);
        if (star.isPeriodical())
        {
            info += tr("<h3>Okres: %1</h3>"
                    "<h3>T<sub>0</sub>: %2</h3>").
                    arg(star.getPeriod(), 0, 'f', 8).
                    arg(star.getEpoch(), 0, 'f', 6);
        }
        ui.txtQuickStarResults->setHtml(info);
    }
    else
    {
        ui.txtQuickStarResults->setHtml(tr("Nie odnaleziono gwiazdy."));
    }
    QApplication::beep();
}

/**
 * Reaguje na błąd komunikacji z GCVS.
 *
 * @param message komunikat błędu
 */
void DogzWindow::gcvsError(QString message)
{
    ui.txtQuickStarResults->setText(message);
    ui.progressQuickStar->hide();
}

/**
 * Odpowiedź na nadejście danych z AAVSO.
 *
 * @param observations lista obserwacji otrzymana z managera
 */
void DogzWindow::aavsoLookupFinished(QList<Dogz::Observation*> observations)
{
    if (observations.isEmpty())
    {
        ui.lblRecentAavsoObs->setText(
                tr("Brak ostatnich obserwacji %1 w AAVSO.").
                arg(ui.editQuickStarSearch->text().toUpper()));
    }
    else
    {
        ui.tabQuickStarSearch->setTabText(1,
                tr("AAVSO (%1)").arg(observations.size()));

        int rowCount = qMin(5, observations.size()); // TODO: odczyt min. z ustawień
        ui.tblRecentAavsoObs->setRowCount(rowCount);

        // dodawanie wiersz po wierszu
        for (int i = 0; i < rowCount; ++i)
        {
            QTableWidgetItem* item;
            // JD
            item = new QTableWidgetItem;
            item->setText(QString::number(observations[i]->getJd(), 'f', 4));
            ui.tblRecentAavsoObs->setItem(i, 0, item);
            // data
            item = new QTableWidgetItem;
            item->setText(JdTools::jdToDate(observations[i]->getJd()).
                    toString("dd-MM hh:mm"));
            ui.tblRecentAavsoObs->setItem(i, 1, item);
            // jasność
            item = new QTableWidgetItem;
            item->setText(QString::number(
                    observations[i]->getMagnitude()));
            ui.tblRecentAavsoObs->setItem(i, 2, item);
            // uwagi/filtr
            item = new QTableWidgetItem;
            item->setText(observations[i]->getNotes());
            ui.tblRecentAavsoObs->setItem(i, 3, item);
        }

        ui.tblRecentAavsoObs->resizeColumnsToContents();
        ui.lblRecentAavsoObs->setText(tr("<b>Ostatnie %1 obserwacji %2</b>").
                arg(rowCount).arg(ui.editQuickStarSearch->text().toUpper()));
    }
}

/**
 * Reaguje na błąd komunikacji z AAVSO.
 *
 * @param message komunikat błędu
 */
void DogzWindow::aavsoError(QString message)
{
    ui.lblRecentAavsoObs->setText(message);
}
